/*
 *    Copyright (c) 2018-2025, shenghua All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: shenghua
 */

package com.chen.base.module.redis.config;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import com.chen.base.module.redis.inter.RedisMsgListener;
import com.chen.base.utils.SpringContextHolder;
import com.chen.base.utils.basic.StringUtil;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Map;

/**
 * RedisTemplate 配置
 *
 * @author L.cm
 */
@Configuration
@AutoConfigureBefore(name = {
		"org.redisson.spring.starter.RedissonAutoConfiguration",
		"org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration"
})
@Import(SpringContextHolder.class)
public class RedisTemplateConfig {

	@Value("${spring.redis.lettuce.pool.min-idle:10}")
	private Integer minIdle;
	@Value("${spring.redis.lettuce.pool.max-idle:20}")
	private Integer maxIdle;
	@Value("${spring.redis.lettuce.pool.max-active:200}")
	private Integer maxActive;
	@Value("${spring.redis.lettuce.pool.max-wait:5000ms}")
	private String maxWaitStr;
	@Value("${spring.redis.host:127.0.0.1}")
	private String host;
	@Value("${spring.redis.port:6379}")
	private Integer port;
	@Value("${spring.redis.password:}")
	private String password;
	@Value("${spring.redis.json-database:6}")
	private Integer jsonDatabase;
	@Resource
	private RedisConnectionFactory redisConnectionFactory;

	@Bean
	@Primary
	public RedisTemplate<String, Object> redisTemplate() {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(redisConnectionFactory);

		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());

		redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
		redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());

		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

	/**
	 * 创建连接池工厂
	 */
	public LettuceConnectionFactory jsonLettuceConnectionFactory() {
		GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
		// 最小空闲数 / 最大空闲数
		poolConfig.setMinIdle(minIdle);
		poolConfig.setMaxIdle(maxIdle);
		// 最大连接数 / 最大阻塞等待时间
		poolConfig.setMaxTotal(maxActive);
		poolConfig.setMaxWait(Duration.ofMillis(Long.parseLong(StringUtil.extractNum(maxWaitStr))));
		// 创建redis单机实例
		RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration();
		standaloneConfig.setDatabase(jsonDatabase);
		standaloneConfig.setHostName(host);
		standaloneConfig.setPort(port);
		if (StrUtil.isNotBlank(password)){
			standaloneConfig.setPassword(RedisPassword.of(password));
		}

		LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration
				.builder().poolConfig(poolConfig).build();

		LettuceConnectionFactory factory = new LettuceConnectionFactory(standaloneConfig, clientConfig);
		// 重新初始化工厂
		factory.afterPropertiesSet();
		return factory;
	}

	/**
	 * 获取redis模板实例
	 */
	@Bean
	public RedisTemplate<String, Object> jsonRedisTemplate() {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(jsonLettuceConnectionFactory());

		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());

		redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());
		redisTemplate.setHashValueSerializer(new GenericFastJsonRedisSerializer());

		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

	/**
	 * redis消息发布/订阅配置
	 */
	@Bean
	public RedisMessageListenerContainer redisMessageListenerContainer(){
		RedisMessageListenerContainer container = new RedisMessageListenerContainer();
		container.setConnectionFactory(redisConnectionFactory);
		// 消息消费者Map
		Map<String, RedisMsgListener> msgListenerMap = SpringContextHolder.getAppContext()
				.getBeansOfType(RedisMsgListener.class);
		for (RedisMsgListener msgListener : msgListenerMap.values()) {
			container.addMessageListener(msgListener,new PatternTopic(msgListener.channelName()));
		}

		return container;
	}
}
